package es.udc.acs.banco.sesion;

import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;

import es.udc.acs.banco.operations.DBSesionBancoOperations;
import es.udc.acs.banco.sesion.canal.*;
import es.udc.acs.banco.util.GestorProperties;
import es.udc.acs.database.exception.ConexionInvalidaException;
import es.udc.acs.parser.ParserMensaje;
import es.udc.acs.utils.CodigosFAP;

public class Sesion implements Serializable {
	
	private static final long serialVersionUID = 6524225069087503222L;
	
	private String _banco;
	private SesionState _estado;
	private int _reintegros = 0;
	private int _abonos     = 0;
	private int _traspasos  = 0;
	private ArrayList<Canal> _canales = new ArrayList<Canal>();
	
	private boolean _forzarRecuperacion = false;
	private boolean _modoVisualizar     = false;
	private ArrayList<Integer> _canalesBloqueados =  new ArrayList<Integer>();
	
	public Sesion()  {
		
		_estado = new SesionCerrada();
		_banco  = GestorProperties.devolverPropiedad(GestorProperties.IDBANCO);
		int ncanales = Integer.parseInt(
				GestorProperties.devolverPropiedad(GestorProperties.NUMCANALES));
		
	    int i;
	    for(i = 0; i<ncanales; i++) {
	    	_canales.add(new Canal());
	    }
	}
	
	
	public synchronized void sumarReintegro() throws SQLException, 
													 IOException, 
													 ConexionInvalidaException {
		
		_reintegros++;
	    DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}	
	
	public synchronized int obtenerReintegros() {
		
		return _reintegros;
	}
	
	
	public synchronized void sumarAbono() throws SQLException, 
												 IOException, 
												 ConexionInvalidaException {
		
		_abonos++;
		DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}	
	
	public synchronized int obtenerAbonos() {
		
		return _abonos;
	}
	
	
	public synchronized void sumarTraspaso() throws SQLException, 
													IOException, 
													ConexionInvalidaException {
		
		_traspasos++;
		DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}
	
	
	public synchronized int obtenerTraspasos() {
		
		return _traspasos;
	}
	
	
	boolean establecerEstado(SesionState estado) {
		
		SesionState aux = _estado;
		
		_estado = estado;
		
		try {
			DBSesionBancoOperations.guardarSesionBanco(_banco, this);
			return true;
		} catch (Exception e) {
			_estado = aux;
			return false;
		}
	}
	
	
	// al establecer estado dentro de los estados ya se guarda
	public synchronized String obtenerEstado()  {

		return _estado.obtenerEstado();
	}
	
	public synchronized boolean detenerTrafico(){
		
		return _estado.detenerTrafico(this);
	}
	
	public synchronized boolean reanudarTrafico(){
		
		return _estado.reanudarTrafico(this);
	}
	
	public synchronized boolean aperturaSesion(){
		
		return _estado.aperturaSesion(this);
	}
	
	public synchronized boolean cierreSesion(){
		
		return _estado.cierreSesion(this);
	}
	
	public synchronized boolean enRecuperacion(){
		
		return _estado.enRecuperacion(this);
	}
	
	public synchronized boolean finRecuperacion() {
		
		return _estado.finRecuperacion(this);
	}	
	
	public synchronized boolean puedoDetenerTrafico(){
		
		return _estado.puedoDetenerTrafico();
	}
	
	public synchronized boolean puedoReanudarTrafico(){
		
		return _estado.puedoReanudarTrafico();
	}
	
	public synchronized boolean puedoAperturaSesion(){
		
		return _estado.puedoAperturaSesion();
	}
	
	public synchronized boolean puedoCierreSesion(){
		
		return _estado.puedoCierreSesion();
	}
	
	public synchronized boolean puedoEnRecuperacion(){
		
		return _estado.puedoEnRecuperacion();
	}
	
	public synchronized boolean puedoFinRecuperacion() {
		
		return _estado.puedoFinRecuperacion();
	}
	
	public synchronized String procesarRDatos(String subcabecera) 
			                                  throws SQLException, 
			                                         IOException, 
			                                         ConexionInvalidaException {		

		 String codigo = _estado.procesarRDatos();
		 boolean recuperacion = obtenerEstado().equals("recuperacion");

		 // el estado no permite responder
		 if (!codigo.equals(CodigosFAP.CE_CORRECTO)) {
		 	return codigo;
		 }
		 
		 int canal = Integer.parseInt(ParserMensaje.obtenerCanal(subcabecera));
		 int mensajeNum =
				 Integer.parseInt(ParserMensaje.obtenerNumMensaje(subcabecera));		 
		 
		 Canal c = _canales.get(canal);
		 
		 // recuperacion, si el mensaje es el mismo que el ultimo se 
		 // vuelve a enviar
		 if ( recuperacion 
				 && _canalesBloqueados.contains(canal)
				 && (mensajeNum != c.obtenerNumMensaje())) {
			 return CodigosFAP.CE_MSN_CANAL_OCUPADO;
		 }

		 // bloqueamos el primer canal, siempre existira
		 if (_forzarRecuperacion && (canal == 0)) {
			 return CodigosFAP.NO_RESPUESTA;	
		 }		 
		 // modo recuperacion -> respuesta contestada se reenvia
		 if (recuperacion)  {
			 if (c.estaLibre() && (c.obtenerNumMensaje() == mensajeNum)) {
				 return CodigosFAP.RE_RESPUESTA;  //devolver la respuesta
			 }
		 }		 
		 // canal ocupado
		 if (!c.estaLibre()) {
			 return CodigosFAP.CE_MSN_CANAL_OCUPADO;	
		 }
		 // mensaje fuera de secuencia
		 if (c.obtenerNumMensaje() != (mensajeNum-1)) {
			 return CodigosFAP.CE_MSN_FUERA_SECUENCIA;	
		 }
		 
		 c.nuevoMensaje();		 
		 DBSesionBancoOperations.guardarSesionBanco(_banco, this);
		 		 
		 return codigo; // CodigosFAP.CE_CORRECTO
	}
	
	
	public synchronized String procesarRControl(String tipo) {
		
		return _estado.procesarRControl(tipo);
	}

	
	public synchronized void guardarRespuesta(String subcabecera,
			                                  String mensaje) 
			                                  throws SQLException, 
			                                         IOException, 
			                                         ConexionInvalidaException {
		
		 int canal = Integer.
				 parseInt(ParserMensaje.obtenerCanal(subcabecera));
		 
		 Canal c = _canales.get(canal);
		 
		 c.nuevaRespuesta();
		 c.guardarUltimaRespuesta(mensaje);
		 
		 // recuperacion
		 if (obtenerEstado().equals("recuperacion")) {
			 _canalesBloqueados.add(canal);
		 }
		 
		 DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}
	
	
	/**
	 * Para el caso de modo recuperacion con respuesta contestada
	 */
	public synchronized String obtenerRespuesta(String subcabecera) 
								            	throws SQLException, 
								                   	   IOException, 
								                   	   ConexionInvalidaException {
								
		int canal = Integer.
				parseInt(ParserMensaje.obtenerCanal(subcabecera));
		
		Canal c = _canales.get(canal);
		
		return c.obtenerUltimaRespuesta();
	}


	public synchronized void mostrarEstadoCanales() {
		
		int i = 1;
		for (Canal c : _canales) {
			
			System.out.println("Canal:"+i+
					" Libre:"+c.estaLibre()+
					" UltimaPregunta:"+c.obtenerNumMensaje()+
					" Respuesta:"+c.obtenerUltimaRespuesta());
			i++;
		}
	
	}


	public synchronized boolean isForzarRecuperacion() {
		return _forzarRecuperacion;
	}


	public synchronized void setForzarRecuperacion(boolean op) 
												   throws SQLException, 
												          IOException, 
												          ConexionInvalidaException {
		
		if (!_estado.obtenerEstado().equals("recuperacion")) {
			
			_forzarRecuperacion = op;		
			DBSesionBancoOperations.guardarSesionBanco(_banco, this);
		}
	}


	public synchronized boolean isModoVisualizar() {
		return _modoVisualizar;
	}


	public synchronized void setModoVisualizar(boolean op) 
			                                   throws SQLException, 
			                                          IOException, 
			                                          ConexionInvalidaException {
		
		_modoVisualizar = op;		
		 DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}	
	
	
	public synchronized void limpiarCanalesBloqueados() throws SQLException, 
	                                                           IOException, 
	                                                           ConexionInvalidaException {
		
		_canalesBloqueados = new ArrayList<Integer>();
		 DBSesionBancoOperations.guardarSesionBanco(_banco, this);
	}

}
